#pragma once

#include <iostream>
#include <sys/epoll.h>
#include <unistd.h>
#include "Sock.hpp"

using namespace ns_sock;

namespace ns_epollserver {

    class EpollServer {
    private:
        uint16_t _port;
        int _listen_sock;
        int _epfd;
    public:
        EpollServer(uint16_t port = 8080)
            :_port(port)
            ,_listen_sock(-1)
            ,_epfd(-1)
        {}
        void InitServer() {
            _listen_sock = Sock::Socket();
            Sock::Bind(_listen_sock, _port);
            Sock::Listen(_listen_sock);
            _epfd = epoll_create(128);
            if (_epfd <  0) {
                std::cerr << "create epoll model error" << std::endl;
                exit(4);
            }
        }

        void HandlerServer(struct epoll_event *revs, int num) {
            for (int i = 0; i < num; ++i) {
                if (revs[i].events == EPOLLIN) {
                    if (revs[i].data.fd == _listen_sock) {
                        // 有新的套接字到来
                        struct sockaddr_in peer;
                        socklen_t len = sizeof(peer);
                        int sock = accept(revs[i].data.fd, (struct sockaddr *)&peer, &len);
                        if (sock < 0) {
                            std::cerr << "accept error" << std::endl;
                            continue;
                        }
                        // 把新的套接字加入epoll模型中
                        struct epoll_event ev;
                        ev.events = EPOLLIN;
                        ev.data.fd = sock;
                        epoll_ctl(_epfd, EPOLL_CTL_ADD, sock, &ev);
                        std::cout << "新的sock到来，sock: " << sock << std::endl;
                    } else {
                        // sock上有数据到来
                        char buffer[1024];
                        int s = recv(revs[i].data.fd, buffer, sizeof(buffer) - 1, 0);
                        if (s > 0) {
                            // 读取数据成功
                            buffer[s] = 0;
                            std::cout << "client say# " << buffer << std::endl;
                            struct epoll_event new_ev;
                            new_ev.data.fd = revs[i].data.fd;
                            new_ev.events = revs[i].events | EPOLLOUT;
                            epoll_ctl(_epfd, EPOLL_CTL_MOD, revs[i].data.fd, &new_ev);
                        } else if (s == 0) {
                            // client端关闭
                            close(revs[i].data.fd);
                            std::cout << "client quit: " << revs[i].data.fd << std::endl;
                            epoll_ctl(_epfd, EPOLL_CTL_DEL, revs[i].data.fd, nullptr);

                        } else {
                            // read error
                            std::cerr << "read error" << std::endl;
                            close(revs[i].data.fd);
                            epoll_ctl(_epfd, EPOLL_CTL_DEL, revs[i].data.fd, nullptr);
                        }
                    }
                }
                // 只要上面recv数据后设置了关心写事件，就一定要写这个对写事件的处理。
                // 因为设置了关心写事件后，要是不在写事件处里面把关心的事件改为只关心读事件
                // 那么，epoll_wait时就会一直认为写事件就绪，就会刷屏 wait success
                // read会读取telnet的换行符，telnet的换行符是/t/n
                if (revs[i].events == EPOLLOUT) {
                    std::string out = "ok";
                    send(revs[i].data.fd, out.c_str(), out.size(), 0);
                    struct epoll_event ev;
                    ev.data.fd = revs[i].data.fd;
                    ev.events = EPOLLIN;
                    epoll_ctl(_epfd, EPOLL_CTL_MOD, revs[i].data.fd, &ev);
                }
            }
        }
        
        void Loop() {
            struct epoll_event ev;
            ev.data.fd = _listen_sock;
            ev.events = EPOLLIN;
            if (epoll_ctl(_epfd, EPOLL_CTL_ADD, _listen_sock, &ev) < 0) {
                std::cerr << "ctl epoll error" << std::endl;
                exit(5);
            }
#define NUM 20
            struct epoll_event revs[NUM];
            int timeout = -1;
            while (true) {
                int num = epoll_wait(_epfd, revs, NUM, timeout);
                switch (num) {
                    case 0:
                        std::cout << "time out " << std::endl;
                        break;
                    case -1:
                        std::cout << "epoll wait error  " << std::endl;
                        break;
                    default:
                        std::cout << "wait success " << std::endl;
                        HandlerServer(revs, num);
                        break;
                }
            }
        }
        ~EpollServer() {
            if (_listen_sock >= 0) {
                close(_listen_sock);
            }
            if (_epfd <  0) {
                close(_epfd);
            }
        }
    };
}
















